package cn.kgc.springboot.shiro;

import cn.kgc.springboot.entity.*;
import cn.kgc.springboot.mapper.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: hengge
 * @version: v1.0
 * @DATE: 2022/12/1
 * 自定义域对象realm
 * 完成数据的操作:
 *  (1)判定有没有这个用户
 *  (2)当前用户有什么权限
 */
public class CustomerRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private RolePersMapper rolePersMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    //1.授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //获取身份信息
        String primaryPrincipal = (String) principals.getPrimaryPrincipal();

        /*
         *获取用户身份信息-->查询用户角色名称(role_name)和权限字符串(permission)
         *即用户id(user表)-->权限字符串permission(permission表)
         * */
        //1.根据用户名(username)，查询用户id(id) -->操作user用户表
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("username", primaryPrincipal);
        User user = userMapper.selectOne(userQueryWrapper);
        Integer userId = user.getId();


        //2.根据用户id(user_id),查询角色id(role_id)  -->操作user_role用户角色关联表
        QueryWrapper<UserRole> userRoleQueryWrapper = new QueryWrapper<>();
        userRoleQueryWrapper.in("user_id", userId);
        List<UserRole> userRoles = userRoleMapper.selectList(userRoleQueryWrapper);
        //将userRoles对象类型-->转换为集合类型
        /*
         * System.out.println("userRoles = " + userRoles);
         * 对象类型: userRoles = [UserRole(userId=24, roleId=1), UserRole(userId=24, roleId=2)]
         *
         * System.out.println("roleIds = " + roleIds);
         * 集合类型: roleIds = [1, 2]
         * */
        List<Integer> roleIds = userRoles.stream().map(userRole -> {
            return userRole.getRoleId();
        }).collect(Collectors.toList());
        System.out.println("roleIds = " + roleIds);

        //2.1.根据角色id(role_id),查询角色名字(role_name)
        List<Role> roles = roleMapper.selectBatchIds(roleIds);
        //将roles对象类型-->转换为集合类型
        List<String> roleNames = roles.stream().map(role -> {
            return role.getRoleName();
        }).collect(Collectors.toList());
        System.out.println("roleNames = " + roleNames);


        //3.根据角色id(role_id),查询权限id(per_id)
        QueryWrapper<RolePers> rolePersQueryWrapper = new QueryWrapper<>();
        rolePersQueryWrapper.in("role_id", roleIds);
        List<RolePers> rolePers = rolePersMapper.selectList(rolePersQueryWrapper);
        //将rolePers对象类型-->转换为集合类型
        List<Integer> perIds = rolePers.stream().map(rp -> {
            return rp.getPerId();
        }).collect(Collectors.toList());
        System.out.println("perIds = " + perIds);


        //4.根据权限id(id)，查询权限字符串(permission)
        QueryWrapper<Permission> permissionQueryWrapper = new QueryWrapper<>();
        permissionQueryWrapper.in("id", perIds);
        List<Permission> permissions = permissionMapper.selectList(permissionQueryWrapper);
        //遍历集合，获取权限字符串
        List<String> permissionStr = permissions.stream().map(permission -> {
            return permission.getPermission();
        }).filter(pstr -> pstr != null).collect(Collectors.toList());
        /*
        * 注：
        *   filter是过滤，因为一级目录和二级菜单是没有权限字符串的，只有按钮是拥有权限字符串的，
        *   所以要将为空的数据进行过滤掉，要不然将空的值复制给permissinStr是没有意义的。
        *
        * */
        System.out.println("permissionStr = " + permissionStr);

        //封装成SimpleAuthorizationInfo对象
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //基于权限字符串的权限控制
        //添加权限字符串授权
        simpleAuthorizationInfo.addRoles(roleNames);
        simpleAuthorizationInfo.addStringPermissions(permissionStr);


        return simpleAuthorizationInfo;
    }

    //2.认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //查询数据库，获取用户的身份信息
        String principal = (String) token.getPrincipal();

        //查询用户完整的身份信息
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("username", principal);
        User user = userMapper.selectOne(userQueryWrapper);

        //判断user是否为空
        if(user!=null){

            /* 判定用户是否被锁定
             * 如果获取的状态为0，则账号被锁定，抛出LockedAccountException异常(锁定账号异常)
             * */
            if(user.getState()==0){
                //锁定账户异常
                throw new LockedAccountException();
            }

            //将根据用户名查询的数据结果封装成AuthenticationInfo类型的对象返回，用于密码的校验
            /*
             * 参数1:用户名
             * 参数2:密码
             * 参数3:salt
             * 参数4:域对象realm的名字
             * */
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getSalt()), this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

